package com.example.app_emu.core

import android.util.Log
import com.example.app_emu.core.control.DebugRPC
import com.example.lib_core.util.HexString
import com.example.lib_core.util.JsonUtils
import java.io.IOException
import java.net.DatagramPacket
import java.net.DatagramSocket
import java.net.InetAddress
import java.net.SocketException

class EmulatorDebugLink(val option: Option) {

    private var udpSocket: DatagramSocket? = null
    private var protocolThread: Thread? = null
    private var onLinkListener: OnLinkListener? = null

    var isOpened = false
        private set

    private var remoteAddress: InetAddress = InetAddress.getByName("127.0.0.1")
    private var remotePort: Int = 5354

    private val protocolRunnable = Runnable {
        debug("start process thread")
        val buffer = ByteArray(1024 * 16)
        val packet = DatagramPacket(buffer, buffer.size)
        while (isOpened) {
            try {
                val socket = udpSocket
                if (socket == null) {
                    Thread.sleep(500)
                    debug("socket is null")
                    continue
                }

                socket.receive(packet)

                val length = packet.length
                if (length <= 0) {
                    Thread.sleep(50)
                    debug("recv $length bytes")
                    continue
                }

                remoteAddress = packet.address
                remotePort = packet.port
                Log.e("xiao","remoteAddress:$remoteAddress")
                Log.e("xiao","remotePort:$remotePort")

                debug("recv $length bytes: ${HexString.valueOf(buffer, 0, length, " ")}")

                onLinkListener?.onReceived(buffer.copyOf(length))
            }catch (e: IOException) {
                e.printStackTrace()
            }
        }
        debug("process thread exited")
    }

    @Throws(LinkException::class)
    fun open() {
        val option = option
        debug("open()")
        try {
            if (isOpened) throw LinkException("already opened")
            udpSocket = try {
                DatagramSocket(option.emulatorPort)
            } catch (e: SocketException) {
                throw LinkException("Open link failed.", e)
            }
            Thread.sleep(50)
            // start receive and process threads
            val address = udpSocket?.inetAddress
            Log.e("xiao","service socket address:$address")
            isOpened = true
            protocolThread = Thread(protocolRunnable)
            protocolThread!!.start()
            Thread.sleep(100)
        }catch (e: LinkException) {
            throw e
        } catch (e: Exception) {
            throw LinkException(e)
        }
    }

    fun close() {
        debug("close()")
        isOpened = false
        if (udpSocket != null) {
            udpSocket!!.close()
            udpSocket = null
        }
        if (protocolThread != null) {
            debug("interrupt process thread")
            protocolThread!!.interrupt()
            try {
                protocolThread!!.join()
            } catch (e: InterruptedException) {
                e.printStackTrace()
            }
            protocolThread = null
        }
    }

    @Throws(LinkException::class)
    fun sendTo(buffer: ByteArray) {
        if (!isOpened) {
            debug("link is closed")
            return
        }

        try {
            write(buffer, 0, buffer.size)
        } catch (e: Exception) {
            throw LinkException(e)
        }
    }


    private fun write(buffer: ByteArray, start: Int = 0, length: Int = buffer.size) {
        val socket = udpSocket
        if (socket == null) {
            debug("socket is null")
            return
        }

        val sendBuffer = ByteArray(length)
        System.arraycopy(buffer, start, sendBuffer, 0, length)

        val packet = DatagramPacket(sendBuffer, sendBuffer.size)
        packet.setData(sendBuffer, 0, sendBuffer.size)
        packet.address = InetAddress.getLocalHost()
        packet.port = remotePort

        try {
            socket.send(packet)
            debug("send $length bytes to ${packet.address}: ${HexString.valueOf(buffer)}")
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    fun setOnLinkListener(listener: OnLinkListener): EmulatorDebugLink {
        this.onLinkListener = listener
        return this
    }

    private fun debug(message: String) {
        if (DEBUG) println("EMU-DEBUG: $message")
    }


    class Option {
        var emulatorAddress: InetAddress = InetAddress.getByName("172.16.2.12")
        var emulatorPort: Int = 7700
    }

    companion object {
        var DEBUG = false
    }
}